﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Util.ActionUtils
{
    // V 1.0.4 - 2021-11-16 17:05:48
    // 1. 备份 1.0.3 版本的方法 为 Debounce_Backup_2021_11_16
    // 2. 加上参数 isImmediate ( 马上执行业务逻辑 )
    // 
    // V 1.0.3 - 2021-09-28 09:42:19
    // 因为很多情况适用, 故从 Util.HowesDOMO 项目中提取出来。
    //
    // V 1.0.2 - 2020-2-19 16:04:06
    // 改进代码, 适合与 WinForm 与 WPF
    //
    // V 1.0.0 - 2020-2-14 18:03:42
    // 首次创建

    /// <summary>
    /// <para>当调用 Debonce n毫秒后，才会执行该动作。若在这n毫秒内又再调用，则将重新计算执行时间</para>
    /// <para>形象地解释：如果用手指一直按住一个弹簧，它将不会弹起直到你松手为止。</para>
    /// </summary>
    public class DebounceAction
    {
        System.Timers.Timer mDebounceTimer;

        /// <summary>
        /// ( Winform 适用 ) 延迟指定时间后执行。 在此期间如果再次调用，则重新计时
        /// </summary>
        /// <param name="interval">执行延迟周期（单位毫秒）</param>
        /// <param name="action">定义执行逻辑</param>
        /// <param name="syncInvoke">同步对象，一般为控件。 如不需同步可传null</param>
        /// <param name="isImmediate">马上执行业务逻辑, 默认值 否</param>
        public void Debounce(double interval, Action action, System.ComponentModel.ISynchronizeInvoke syncInvoke, bool isImmediate = false)
        {
            lock (this)
            {
                bool invokeNow = isImmediate == true && mDebounceTimer == null;

                if (invokeNow == true)
                {
                    #region 创建一个没有业务逻辑的Timer

                    mDebounceTimer = new System.Timers.Timer(interval);

                    mDebounceTimer.AutoReset = false;
                    mDebounceTimer.Elapsed += (o, e) =>
                    {
                        mDebounceTimer.Stop();
                        mDebounceTimer.Close();
                        mDebounceTimer = null;

                        #region 由于 isImmediate == true, 一开始就执行了业务逻辑, 所以 timer 只负责倒计时, 若在 interval 内多次执行, 则重新进行 timer

                        //if (dispatcher != null && dispatcher.Thread.IsBackground == false)
                        //{
                        //    dispatcher.Invoke(action, null);
                        //}
                        //else
                        //{
                        //    action.Invoke();
                        //}

                        #endregion
                    };

                    mDebounceTimer.Start();

                    #endregion

                    #region 马上执行业务逻辑

                    if (syncInvoke != null && syncInvoke.InvokeRequired == true)
                    {
                        syncInvoke.Invoke(action, null);
                    }
                    else
                    {
                        action.Invoke();
                    }

                    #endregion                    
                }
                else if (isImmediate == false)
                {
                    if (mDebounceTimer == null)
                    {
                        mDebounceTimer = new System.Timers.Timer(interval);
                        mDebounceTimer.AutoReset = false;
                        mDebounceTimer.Elapsed += (o, e) =>
                        {
                            mDebounceTimer.Stop();
                            mDebounceTimer.Close();
                            mDebounceTimer = null;

                            if (syncInvoke != null && syncInvoke.InvokeRequired == true)
                            {
                                syncInvoke.Invoke(action, null);
                            }
                            else
                            {
                                action.Invoke();
                            }
                        };
                    }
                }

                mDebounceTimer.Stop();
                mDebounceTimer.Start();
            }
        }

        #region 旧版代码

        ///// <summary>
        /////  ( WPF 适用 ) 延迟指定时间后执行。 在此期间如果再次调用，则重新计时
        ///// </summary>
        ///// <param name="interval">执行延迟周期（单位毫秒）</param>
        ///// <param name="action">定义执行逻辑</param>
        ///// <param name="dispatcher">通常设置 null 或 Application.Current.Dispatcher</param>
        //public void Debounce(double interval, Action action, System.Windows.Threading.Dispatcher dispatcher = null)
        //{
        //    lock (this)
        //    {
        //        if (mDebounceTimer == null)
        //        {
        //            mDebounceTimer = new System.Timers.Timer(interval);
        //            mDebounceTimer.AutoReset = false;
        //            mDebounceTimer.Elapsed += (o, e) =>
        //            {
        //                mDebounceTimer.Stop();
        //                mDebounceTimer.Close();
        //                mDebounceTimer = null;

        //                if (dispatcher != null && dispatcher.Thread.IsBackground == false)
        //                {
        //                    dispatcher.Invoke(action, null);
        //                }
        //                else
        //                {
        //                    action.Invoke();
        //                }
        //            };
        //        }
        //        mDebounceTimer.Stop();
        //        mDebounceTimer.Start();
        //    }
        //}

        /// <summary>
        /// ( Winform 适用 ) 延迟指定时间后执行。 在此期间如果再次调用，则重新计时
        /// </summary>
        /// <param name="interval">执行延迟周期（单位毫秒）</param>
        /// <param name="action">定义执行逻辑</param>
        /// <param name="syncInvoke">同步对象，一般为控件。 如不需同步可传null</param>
        public void Debounce_Backup_2021_11_16(double interval, Action action, System.ComponentModel.ISynchronizeInvoke syncInvoke)
        {
            lock (this)
            {
                if (mDebounceTimer == null)
                {
                    mDebounceTimer = new System.Timers.Timer(interval);
                    mDebounceTimer.AutoReset = false;
                    mDebounceTimer.Elapsed += (o, e) =>
                    {
                        mDebounceTimer.Stop();
                        mDebounceTimer.Close();
                        mDebounceTimer = null;

                        if (syncInvoke != null && syncInvoke.InvokeRequired == true)
                        {
                            syncInvoke.Invoke(action, null);
                        }
                        else
                        {
                            action.Invoke();
                        }
                    };
                }
                mDebounceTimer.Stop();
                mDebounceTimer.Start();
            }
        }

        #endregion
    }
}
